package haxeframe.views;

import haxe.Timer;

import haxeframe.events.ViewEvent;
import haxeframe.views.View;

import flash.display.Stage;
import flash.events.EventDispatcher;

import flash.Lib;
import flash.system.System;

class ViewManager
{
	public static var views:Map<String, Class<Dynamic>> = new Map<String, Class<Dynamic>>();
	public static var states:Map<String, Array<String>> = new Map<String, Array<String>>();

	private static var showing:Map<String, View> = new Map<String, View>();
	private static var hiding:Map<String, View> = new Map<String, View>();

	public static var viewInstances:Map<String, Dynamic> = new Map<String, Dynamic>();

	private static var dispatcher:EventDispatcher = new EventDispatcher();
		
	public static var statesShowing:Map<String, Bool> = new Map<String, Bool>();
	public static var currentState:String;
	public static var previousState:String;
		
	public static var beforeShow:String->Bool;
	public static var onShow:String->Void;
	
	public static var beforeHide:String->Bool;
	public static var onHide:String->Void;

	public static var scope:Dynamic;

	private static var showOnHide:String = '';

	private static var hidingState:String = '';

	public static function reset():Void
	{
		views = new Map<String, Class<Dynamic>>();
		states = new Map<String, Array<String>>();
		showing = new Map<String, View>();
		hiding = new Map<String, View>();
		viewInstances = new Map<String, Dynamic>();
		dispatcher = new EventDispatcher();
		statesShowing = new Map<String, Bool>();
		System.gc();
	}

	public static function addView(viewName:String, viewClass:Class<Dynamic>):Void
	{
		if(views.exists(viewName)){
			// trace('WARNING: Overwriting existing view "'+viewName+'"');
		}
		views.set(viewName, viewClass);
	}

	public static function removeView(viewName:String):Void
	{
		if(views.exists(viewName)){
			views.remove(viewName);
		}
	}

	public static function getViewInstance(viewName:String):View
	{
		if(!viewInstances.exists(viewName)){
			viewInstances.set(viewName, Type.createInstance(views.get(viewName), []));
		}
		return viewInstances.get(viewName);
	}

	public static function showView(viewName:String):Void
	{
		if(showing.get(viewName) != null && hiding.get(viewName) == null)
			return;

		var v:View = getViewInstance(viewName);
		showing.set(viewName, v);
		v.addEventListener(ViewEvent.SHOW_COMPLETE, onShowComplete);
		Lib.current.addChild(v);
		v.show();
	}

	public static function addEventListener(type : String, listener : Dynamic -> Void, ?useCapture : Bool, ?priority : Int, ?useWeakReference : Bool) : Void
	{
		dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
	}

	public static function removeEventListener(type : String, listener : Dynamic -> Void) : Void
	{
		dispatcher.removeEventListener(type, listener);
	}

	public static function hideView(viewName:String):Void
	{
		var v:View = getViewInstance(viewName);
		hiding.set(viewName, v);
		v.addEventListener(ViewEvent.HIDE_COMPLETE, onHideComplete);
		v.hide();
	}

	public static function addState(stateName:String, views:Array<String>):Void
	{
		states.set(stateName, views);
	}

	public static function showState(stateName:String):Void
	{
		var returnVal:Bool = true;
		if(beforeShow != null && scope != null){
			returnVal = Reflect.callMethod(scope, beforeShow, [stateName]);
			if(!returnVal) return;
		}

		var stateViews:Array<Dynamic> = states.get(stateName);
		
		if(stateViews != null) statesShowing.set(stateName, true);

		for(i in states.get(stateName)){
			showView(i);
		}
		if(onShow != null && scope != null){
			Reflect.callMethod(scope, onShow, [stateName]);
		}
	}

	public static function hideState(stateName:String):Void
	{
		if(states.get(stateName) == null) return;
		hidingState = stateName;
		for(i in states.get(stateName)){
			hideView(i);
		}
	}

	public static function hideAndShowStates(hideStateName:String, showStateName:String, simultaneous:Bool=false):Void
	{
		if(simultaneous){
			showState(showStateName);
		} else {
			showOnHide = showStateName;
		}
		hideState(hideStateName);
	
	}

	public static function onShowComplete(event:ViewEvent):Void
	{
		var v:View = event.target;
		v.removeEventListener(ViewEvent.SHOW_COMPLETE, onShowComplete);
		for(k in showing.keys()){
			if(showing.get(k) == v){
				showing.remove(k);
				break;
			}
		}
		if(Lambda.count(showing) == 0){
			dispatcher.dispatchEvent(new ViewEvent(ViewEvent.SHOW_COMPLETE));
		}
	}

	public static function onHideComplete(event:ViewEvent):Void
	{
		var v:View = event.target;
		var viewName:String='';	
		v.removeEventListener(ViewEvent.HIDE_COMPLETE, onHideComplete);
		for(k in hiding.keys()){
			if(hiding.get(k) == v){
				viewName = k;
				hiding.remove(k);
				break;
			}
		}
		if(v.parent != null){
			v.parent.removeChild(v);
		}
		if(Lambda.count(hiding) == 0){
			if(onHide != null && hidingState != null){
				Reflect.callMethod(scope, onHide, [hidingState]);
				hidingState = null;
			}
			dispatcher.dispatchEvent(new ViewEvent(ViewEvent.HIDE_COMPLETE));
			if(showOnHide != ''){
				Timer.delay(function():Void{
					showState(showOnHide);
					showOnHide = '';
				}, 1);
			}
		}
		if(v.deleteOnRemove && viewName != '') viewInstances.remove(viewName);
	}

	public static function clear():Void
	{
		viewInstances = new Map<String, Dynamic>();
		System.gc();
	}

}